
import _ from "lodash";

export const preCheckGraph = (graph:any, jsonData:any)=> {
    let startNodeHasLine = false;
    let endNodeHasLine = false;
    let hasNoLineNode=false;
    // 1. 开始和结束节点，都必须有连线
    // 2. 检查没有被连线的节点, 除了开始和结束节点， 其它节点都应该有开始和结束。 并行任务中的节点例外。
    // 3. 并行分组节点内，必须 有一个只有出连线节点(开始)，有一个只有入连线节点(结束)， 其它节点均有入和出的连线。

    // 1.1 找到开始节点
    let filterStartNodes = jsonData.cells.filter((item:any)=>item.data && item.data.id == 'StartNode');
    if(filterStartNodes && filterStartNodes.length>0){
        const StartNodeId = filterStartNodes[0].id;
        const ports = filterStartNodes[0].ports;
        const portId = ports.items[0].id;
        console.log('StartNodeId & portId', StartNodeId, portId);
        let filterStartEdge = jsonData.cells.filter((item:any)=>item.shape == 'edge' && item.source.cell == StartNodeId && item.source.port == portId);
        if(filterStartEdge && filterStartEdge.length>0){
            startNodeHasLine = true;
        }else{
            alert('[开始]节点没有连线');
        }
    }else{
        alert('没有[开始]节点');
    }

    // 1.2 找到结束节点
    let filterEndNodes = jsonData.cells.filter((item:any)=>item.data && item.data.id == 'EndNode');
    if(filterEndNodes && filterEndNodes.length>0){
        const EndNodeId = filterEndNodes[0].id;
        const ports = filterStartNodes[0].ports;
        const portId = ports.items[0].id;
        console.log('EndNodeId & portId', EndNodeId, portId);
        let filterStartEdge = jsonData.cells.filter((item:any)=>item.shape == 'edge' && item.target.cell == EndNodeId);
        if(filterStartEdge && filterStartEdge.length>0){
            endNodeHasLine = true;
        }else{
            alert('[结束]节点没有连线');
        }
    }else{
        alert('没有[结束]节点');
    }

    // // 2. 检查是否存在未连线节点。
    // let rootNodes = graph.getRootNodes();
    // // Filter
    // // 排除Start节点, 排除被嵌套的节点
    // rootNodes = rootNodes.filter((item:any)=>item.data.id != 'StartNode' && !item._parent)
    // if(rootNodes.length>0){
    //     console.log("有未连线节点", rootNodes);
    //     hasNoLineNode = true;
    //     alert("有未连线节点");
    // }
    return startNodeHasLine && endNodeHasLine && !hasNoLineNode;
}

export const checkGraph = (jsonData:any) => {
    // 1. 第一个节点和最后一个节点必须是开始和结束

    return true;
}


export const converter = (graph:any ,jsonData:any)=>{
    // 从StartNode开始, 一个一个节点查找，直到找到EndNode
    let node_cells = _.cloneDeep(jsonData.cells.filter((item:any)=>item.shape != 'edge'));
    let edge_cells = _.cloneDeep(jsonData.cells.filter((item:any)=>item.shape == 'edge'));

    if(!node_cells || node_cells.length<=0){
        return;
    }
    
    // 1. X6的数据结构，所有的cells是平铺的, 需要改成非平铺。
    // 所以可以通过遍历, 将children中的节点ID，替换成真实的节点
    console.log('replaceChildrenIdWithObject start');
    let replaced_node_celles = replaceChildrenIdWithObject(node_cells);
    console.log('replaceChildrenIdWithObject end', replaced_node_celles);

    // 2. 从Start节点开始查找连接的每个节点，生成一个新的有序数组。
    console.log('Resort Nodes From Start');
    let startNode = findStartNode(node_cells);
    let sortedNodes = [startNode];
    let currentNode = startNode;
    let loop = 0;// 最多找500次。
    while(true){
        if(loop>500) { alert('节点过多'); break;}
        console.log('loop', loop);
        const next = findNextNode(currentNode, node_cells, edge_cells);
        if(next) {
            currentNode = next;
            sortedNodes.push(currentNode);
        }else{
            break;
        }
        loop++;
    }
    console.log('Resort Nodes From Start', sortedNodes);


    // 3. 检查节点和连线是否正确
    // 规则1: 检查新数组的大小是否和原数组大小一致，若不一致，则判定有孤岛节点。
    // 检查2: 第一个节点必须是开始节点， 最后一个节点是结束节点。
    // 检查3: 并行分组内，必须有且只有一个节点是没有output连线的，有且只有一个节点是没有input连线的，其他节点都是有连线的。没有input的节点是开始节点，没有output连线的是结束节点。

    // 规则1
    if(replaced_node_celles.length != sortedNodes.length){
        alert('请确保所有连线正确');
        return;
    }

    // 规则2
    let lastNode = sortedNodes[sortedNodes.length - 1];
    if(lastNode.data.id != 'EndNode'){
        alert('最后一个节点非[结束]节点!');
        return;
    }

    // 规则3 TODO


    // 递归遍历，生成Conductor数据
    const data = convertToConductorData(sortedNodes);

    return data;
}

const convertToConductorData = (nodes:any) => {
    let timestamp = new Date().getTime()
    let workflowName = "workflow_"+timestamp+"_"+_randomString(6);
    let finalData = _initWorkFlowData(workflowName, workflowName);
    let err = null;
    let depth1 = (node:any, nodeList = []) => {
        //node不能为null
        if (node !== null) {
            if(node.data.id == 'StartNode' || node.data.id == 'EndNode'){

            }else if(node.data.id == 'NetworkNode') {
                let data = _genHttpTaskData("http://127.0.0.1:8181/xphoto/api/health/status", "GET");
                nodeList.push(data as never);
            }else if(node.data.id == 'VariableNode') {
                // nodeList.push(node as never)
            }else if(node.data.id == 'ForkNode'){
                let children = node.children || []
                let forkTasksData = _genForkTaskData();
                // Fork节点中，直接子节点只能是ForkGroup, 所有任务，必须在ForkGroup中。
                // 每个ForkGroup之间是并行执行的， ForkGroup内，是顺序执行
                if(children.length>0){
                    // 1. 都是forkGroup的情况
                    let list: never[] | undefined = [];
                    for (let i = 0; i < children.length; i++) {
                        let nodes = children[i].nodes || []
                        let sublist: never[] | undefined = [];
                        for (let j = 0; j < nodes.length; j++) {
                            const node = nodes[j];
                            //递归调用
                            depth1(node, sublist)
                        }
                        list.push(sublist as never);
                    }
                    forkTasksData.forkTasks = list;
                    nodeList.push(forkTasksData as never);

                    // 生成Join
                    let joinTasksData = _genJoinTaskData();
                    let refNames: any[] = [];
                    list.forEach((forkGroup:any, idx)=>{
                        let names = forkGroup.map((item:any)=>item.taskReferenceName);
                        refNames = refNames.concat(names);
                    })
                    joinTasksData.joinOn = refNames as never;
                    nodeList.push(joinTasksData as never);
                }else{
                    // 2. 不存在分组，报错
                    err = new Error("并行任务重不存在分组，请添加分组");
                    alert(err.message);
                }
            }
        }
        return nodeList
    }
    let taskList: never[] | undefined = [];
    console.log('开始深度遍历');

    for (let index = 0; index < nodes.length; index++) {
        const node = nodes[index];
        depth1(node, taskList);
    }
    
    finalData.tasks = taskList;

    return finalData
}

// 将children中的节点ID，替换成真实的节点
const replaceChildrenIdWithObject = (node_cells:any)=>{
    let childCellIds: string[] = [];
    for (let index = 0; index < node_cells.length; index++) {
        const node_cell = node_cells[index];
        if(node_cell.children) {
            let newChildren: any[] = [];
            node_cell.children.forEach((childCellId:string) => {
                childCellIds.push(childCellId);
                const childCell = findNodeById(childCellId, node_cells);
                newChildren.push(childCell);
            });
            node_cell.children = newChildren;
        }
    }

    // 从 node_cells 中过滤掉拥有父节点的节点
    let new_node_cells = node_cells.filter((item:any) => childCellIds.indexOf(item.id)<0)
    return new_node_cells;
}

const findStartNode = (node_cells:any) => {
    let filterStartNodes = node_cells.filter((item:any)=>item.data && item.data.id == 'StartNode');
    if(filterStartNodes && filterStartNodes.length >0){
        return filterStartNodes[0];
    }else{
        return null;
    }
}

const findNodeById = (id:string, node_cells:any)=>{
    let cellFilter = node_cells.filter((item:any)=>item.id == id);
    if(cellFilter && cellFilter.length>0){
        return cellFilter[0];
    }else{
        return  null;
    }
}

const findNextNode = (node_cell:any, node_cells:any, edge_cells:any)=>{
    let cellId = node_cell.id;
    if(!(node_cell.ports)){ // End节点，没有ports
        return null;
    }
    let filterOutput = node_cell.ports.items.filter((item:any)=>item.group == 'output');
    if(filterOutput && filterOutput.length>0){
        let outputId = filterOutput[0].id;
        let edgeFilter = edge_cells.filter((item:any)=>item.source.cell == cellId && item.source.port == outputId);
        if(edgeFilter && edgeFilter.length>0){
            let edge = edgeFilter[0];
            let nextCellId = edge.target.cell;
            let nextCell = findNodeById(nextCellId, node_cells);
            return nextCell;
        }else{
            return null;
        }
    }else{
        return null;
    }
}

const _initWorkFlowData = (workFlowName:string, description:string="workflow") => {
    return {
        "name": workFlowName,
        "description": description,
        "version": 1,
        "tasks": [
        ],
        "inputParameters": [],
        "outputParameters": {},
        "schemaVersion": 2,
        "restartable": false,
        "workflowStatusListenerEnabled": false,
        "ownerEmail": "example@email.com",
        "timeoutPolicy": "ALERT_ONLY",
        "timeoutSeconds": 0
    };
}

const _genHttpTaskData = (url="http://127.0.0.1:8181/xphoto/api/health/status", method="GET") => {
    let timestamp = new Date().getTime()
    let taskName = "httptask"+timestamp+"_"+_randomString(6);
    return  {
        "name": taskName,
        "taskReferenceName": taskName+"_ref",
        "inputParameters": {
          "http_request": {
            "uri": url,
            "method": method
          }
        },
        "type": "HTTP"
    }
}

const _genForkTaskData = ()=>{
    let timestamp = new Date().getTime()
    let taskName = "forktask"+timestamp+"_"+_randomString(6);
    return {
        "name": taskName,
        "taskReferenceName": taskName+"_ref",
        "inputParameters": {},
        "type": "FORK_JOIN",
        "decisionCases": {},
        "defaultCase": [],
        "forkTasks": [
        ],
        "startDelay": 0,
        "joinOn": [],
        "optional": false,
        "defaultExclusiveJoinTask": [],
        "asyncComplete": false,
        "loopOver": []
    }
}

const _genJoinTaskData = () => {
    let timestamp = new Date().getTime()
    let taskName = "jointask"+timestamp+"_"+_randomString(6);
    return {
        "name": taskName,
        "taskReferenceName": taskName+"_ref",
        "inputParameters": {},
        "type": "JOIN",
        "decisionCases": {},
        "defaultCase": [],
        "forkTasks": [],
        "startDelay": 0,
        "joinOn": [
        ],
        "optional": false,
        "defaultExclusiveJoinTask": [],
        "asyncComplete": false,
        "loopOver": []
      }
}

const _randomString = (length:number) => {
    var str = '0123456789abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ';
    var result = '';
    for (var i = length; i > 0; --i)
        result += str[Math.floor(Math.random() * str.length)];
    return result;
}